
/**
  ******************************************************************************
  * Copyright 2021 The grapilot Authors. All Rights Reserved.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  * 
  * http://www.apache.org/licenses/LICENSE-2.0
  * 
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  * 
  * @file       task_serial.c
  * @author     baiyang
  * @date       2021-7-15
  ******************************************************************************
  */

/*----------------------------------include-----------------------------------*/
#include "task_serial.h"
#include <common/grapilot.h>
#include <common/console/console.h>
#include <parameter/param.h>
/*-----------------------------------macro------------------------------------*/
#define EVENT_SERIAL_LOOP               (1<<0)
/*----------------------------------typedef-----------------------------------*/

/*---------------------------------prototype----------------------------------*/

/*----------------------------------variable----------------------------------*/
struct gp_serial_thread serial[GP_SERIAL_COUNT_MAX];

// 串口通信用线程
static char thread_serial0_task_stack[2048];
struct rt_thread thread_serial0_task_handle;

static char thread_serial1_task_stack[2048];
struct rt_thread thread_serial1_task_handle;

static char thread_serial2_task_stack[2048];
struct rt_thread thread_serial2_task_handle;

static char thread_serial3_task_stack[2048];
struct rt_thread thread_serial3_task_handle;

static char thread_serial4_task_stack[2048];
struct rt_thread thread_serial4_task_handle;

static char thread_serial5_task_stack[2048];
struct rt_thread thread_serial5_task_handle;
/*-------------------------------------os-------------------------------------*/

/*----------------------------------function----------------------------------*/
/**
  * @brief       UART发送完成回调,DMA发送完成释放信号量
  * @param[in]   dev  
  * @param[in]   buffer  
  * @param[out]  
  * @retval      
  * @note        
  */
static rt_err_t _uart_tx_done(rt_device_t dev, void* buffer)
{
    for(uint8_t i = 0; i < 8; i++)
    {
        if(serial[i].sdevice != RT_NULL)
        {
            if(serial[i].sdevice->puart == dev)
            {
                return rt_sem_release(serial[i].tx_sem);
            }
        }
    }
    
    RT_ASSERT(0);
    return RT_FALSE;
}

/**
  * @brief       定时器更新发送事件
  * @param[in]   parameter  传进串口线程结构体指针
  * @param[out]  
  * @retval      
  * @note        
  */
static void timer_update(void *parameter)
{
    gp_serial_thread_t thread = (gp_serial_thread_t) parameter;
    rt_event_send(&thread->event, EVENT_SERIAL_LOOP);
}

/**
  * @brief       循环开始前的初始化
  * @param[in]   thread  
  * @param[out]  
  * @retval      
  * @note        
  */
static void serial_thread_init(gp_serial_thread_t thread)
{
    thread->res = 0;
    thread->recv_set = 0;
    thread->wait_set = EVENT_SERIAL_LOOP;
    thread->sdevice = (gp_serial_device_t)rt_device_find(thread->name);  //查找设备

    RT_ASSERT(thread->sdevice != RT_NULL);

    if(thread->sdevice->oflag & RT_DEVICE_FLAG_DMA_TX) //DMA模式下需要信号量触发回调
    {
        thread->tx_sem= rt_sem_create(thread->sem_name, 0, RT_IPC_FLAG_FIFO);
        rt_device_set_tx_complete(thread->sdevice->puart, _uart_tx_done);
    }

    /* create event */
    thread->res = rt_event_init(&thread->event, thread->event_name, RT_IPC_FLAG_FIFO);

    /* register timer event */
    rt_timer_init(&thread->timer, thread->timer_name,
                    thread->ops->update,
                    thread,
                    rt_tick_from_millisecond(1),
                    RT_TIMER_FLAG_PERIODIC | RT_TIMER_FLAG_HARD_TIMER);
    rt_timer_start(&thread->timer);

}

/**
  * @brief       循环发送和接受
  * @param[in]   thread  
  * @param[out]  
  * @retval      
  * @note        
  */
static void serial_loop(gp_serial_thread_t thread)
{
    //从UART缓冲区读取数据,并写入SERIAL缓冲区rb_rx
    uint32_t cnt = 0;
    while(rt_device_read(thread->sdevice->puart, 0, &thread->buf.rec_data, 1))
    {
        thread->buf.rec_flag = 1;
        rt_ringbuffer_putchar_force(thread->sdevice->rb_rx,thread->buf.rec_data);
        cnt++;
    }

    if(thread->buf.rec_flag == 1)
    {
        if(thread->sdevice->parent.rx_indicate != RT_NULL)
        {
            thread->sdevice->parent.rx_indicate(&thread->sdevice->parent, cnt); //如果上次数据读完，进入接受完成回调
        }
        thread->buf.rec_flag = 0; //接受到数据标志位清空
    }

    // 从SERIAL缓冲区rb_tx读取数据,使用UART驱动发送
    thread->buf.send_len = rt_ringbuffer_data_len(thread->sdevice->rb_tx);
    if( thread->buf.send_len )
    {
        rt_ringbuffer_get(thread->sdevice->rb_tx, thread->buf.send_data, thread->buf.send_len);
        rt_device_write(thread->sdevice->puart, 0, thread->buf.send_data, thread->buf.send_len);

        if (thread->sdevice->parent.open_flag & RT_DEVICE_FLAG_INT_TX)
        {
            if(thread->sdevice->parent.tx_complete != RT_NULL)
            {
                thread->sdevice->parent.tx_complete(&thread->sdevice->parent, RT_NULL); //发送完成回调，测试没问题,目前在event_task线程中测试
            }
        }
#ifdef RT_SERIAL_USING_DMA
        else
        {
            rt_sem_take(thread->tx_sem, RT_WAITING_FOREVER); //等待信号量（表示DMA发送完成）

            if(thread->sdevice->parent.tx_complete != RT_NULL)
            {
                thread->sdevice->parent.tx_complete(&thread->sdevice->parent, RT_NULL); //发送完成回调，测试没问题,目前在event_task线程中测试
            }
        }
#endif /* RT_SERIAL_USING_DMA */

    }
}

const struct gp_serial_thread_ops _ops = {
    .init = serial_thread_init,
    .update = timer_update,
    .loop = serial_loop
};

/*----------------------------------serial0----------------------------------*/
/**
  * @brief       serial0线程主函数
  * @param[in]   parameter  
  * @param[out]  
  * @retval      
  * @note        
  */
void serial0_main(void *parameter)
{
    serial[0].name = "serial0";
    serial[0].event_name = "serial0_event";
    serial[0].timer_name = "serial0_timer";
    serial[0].sem_name = "s0_sem";
    serial[0].ops = &_ops;

    rt_device_t serial_dev = rt_device_find(serial[0].name);
    
    if(serial_dev == RT_NULL)
    {
        return;
    }

    serial[0].ops->init(&serial[0]);

    while(1)
    {
        serial[0].res = rt_event_recv(&serial[0].event, serial[0].wait_set, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, 
                                RT_WAITING_FOREVER, &serial[0].recv_set);
        
        if(serial[0].res == RT_EOK){
            if(serial[0].recv_set & EVENT_SERIAL_LOOP){
                serial[0].ops->loop(&serial[0]);
            }
        }
    }
}

/*----------------------------------serial1----------------------------------*/
/**
  * @brief       serial1线程主函数
  * @param[in]   parameter  
  * @param[out]  
  * @retval      
  * @note        
  */
void serial1_main(void *parameter)
{
    serial[1].name = "serial1";
    serial[1].event_name = "serial1_event";
    serial[1].timer_name = "serial1_timer";
    serial[1].sem_name = "s1_sem";
    serial[1].ops = &_ops;

    rt_device_t serial_dev = rt_device_find(serial[1].name);
    
    if(PARAM_GET_INT8(SERIAL, SERIAL1_PROTOCOL) == SerialProtocol_None \
        || serial_dev == RT_NULL)
    {
        return;
    }

    serial[1].ops->init(&serial[1]);

    while(1)
    {
        serial[1].res = rt_event_recv(&serial[1].event, serial[1].wait_set, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, 
                                RT_WAITING_FOREVER, &serial[1].recv_set);
        
        if(serial[1].res == RT_EOK){
            if(serial[1].recv_set & EVENT_SERIAL_LOOP){
                serial[1].ops->loop(&serial[1]);
            }
        }
    }
}


/*----------------------------------serial2----------------------------------*/

/**
  * @brief       serial2线程主函数
  * @param[in]   parameter  
  * @param[out]  
  * @retval      
  * @note        
  */
void serial2_main(void *parameter)
{
    serial[2].name = "serial2";
    serial[2].event_name = "serial2_event";
    serial[2].timer_name = "serial2_timer";
    serial[2].sem_name = "s2_sem";
    serial[2].ops = &_ops;

    rt_device_t serial_dev = rt_device_find(serial[2].name);

    if(PARAM_GET_INT8(SERIAL, SERIAL2_PROTOCOL) == SerialProtocol_None \
        || serial_dev == RT_NULL)
    {
        return;
    }

    serial[2].ops->init(&serial[2]);

    while(1)
    {
        serial[2].res = rt_event_recv(&serial[2].event, serial[2].wait_set, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, 
                                RT_WAITING_FOREVER, &serial[2].recv_set);
        
        if(serial[2].res == RT_EOK){
            if(serial[2].recv_set & EVENT_SERIAL_LOOP){
                serial[2].ops->loop(&serial[2]);
            }
        }
    }
}

/*----------------------------------serial3----------------------------------*/
/**
  * @brief       serial3线程主函数
  * @param[in]   parameter  
  * @param[out]  
  * @retval      
  * @note        
  */
void serial3_main(void *parameter)
{
    serial[3].name = "serial3";
    serial[3].event_name = "serial3_event";
    serial[3].timer_name = "serial3_timer";
    serial[3].sem_name = "s3_sem";
    serial[3].ops = &_ops;

    rt_device_t serial_dev = rt_device_find(serial[3].name);

    if(PARAM_GET_INT8(SERIAL, SERIAL3_PROTOCOL) == SerialProtocol_None \
        || serial_dev == RT_NULL)
    {
        return;
    }

    serial[3].ops->init(&serial[3]);

#if 0 //测试修改波特率成功，考虑是否封装
    struct serial_configure serial3_config = RT_SERIAL_CONFIG_DEFAULT; 
    serial3_config.baud_rate = BAUD_RATE_230400;
    rt_device_control(rt_device_find(serial[2].name), RT_DEVICE_CTRL_CONFIG, &serial3_config);
#endif

    while(1)
    {
        serial[3].res = rt_event_recv(&serial[3].event, serial[3].wait_set, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, 
                                RT_WAITING_FOREVER, &serial[3].recv_set);
        
        if(serial[3].res == RT_EOK){
            if(serial[3].recv_set & EVENT_SERIAL_LOOP){
                serial[3].ops->loop(&serial[3]);
            }
        }
    }
}

/*----------------------------------serial4----------------------------------*/
/**
  * @brief       serial4线程主函数
  * @param[in]   parameter  
  * @param[out]  
  * @retval      
  * @note        
  */
void serial4_main(void *parameter)
{
    serial[4].name = "serial4";
    serial[4].event_name = "serial4_event";
    serial[4].timer_name = "serial4_timer";
    serial[4].sem_name = "s4_sem";
    serial[4].ops = &_ops;

    rt_device_t serial_dev = rt_device_find(serial[4].name);

    if(PARAM_GET_INT8(SERIAL, SERIAL4_PROTOCOL) == SerialProtocol_None \
        || serial_dev == RT_NULL)
    {
        return;
    }
    
    serial[4].ops->init(&serial[4]);

    while(1)
    {
        serial[4].res = rt_event_recv(&serial[4].event, serial[4].wait_set, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, 
                                RT_WAITING_FOREVER, &serial[4].recv_set);
        
        if(serial[4].res == RT_EOK){
            if(serial[4].recv_set & EVENT_SERIAL_LOOP){
                serial[4].ops->loop(&serial[4]);
            }
        }
    }
}

/*----------------------------------serial5----------------------------------*/
/**
  * @brief       serial5线程主函数
  * @param[in]   parameter  
  * @param[out]  
  * @retval      
  * @note        
  */
void serial5_main(void *parameter)
{
    serial[5].name = "serial5";
    serial[5].event_name = "serial5_event";
    serial[5].timer_name = "serial5_timer";
    serial[5].sem_name = "s5_sem";
    serial[5].ops = &_ops;

    rt_device_t serial_dev = rt_device_find(serial[5].name);

    if(PARAM_GET_INT8(SERIAL, SERIAL5_PROTOCOL) == SerialProtocol_None \
        || serial_dev == RT_NULL)
    {
        return;
    }
    
    serial[5].ops->init(&serial[5]);

    while(1)
    {
        serial[5].res = rt_event_recv(&serial[5].event, serial[5].wait_set, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, 
                                RT_WAITING_FOREVER, &serial[5].recv_set);
        
        if(serial[5].res == RT_EOK){
            if(serial[5].recv_set & EVENT_SERIAL_LOOP){
                serial[5].ops->loop(&serial[5]);
            }
        }
    }
}

rt_err_t task_serial_init(void)
{
    rt_err_t res;

    gp_serial_device_init();

    res = rt_thread_init(&thread_serial0_task_handle,
                           "serial0",
                           serial0_main,
                           RT_NULL,
                           &thread_serial0_task_stack[0],
                           sizeof(thread_serial0_task_stack),PRIORITY_UART,5);
    if (res == RT_EOK)
        rt_thread_startup(&thread_serial0_task_handle);

    res = rt_thread_init(&thread_serial1_task_handle,
                           "serial1",
                           serial1_main,
                           RT_NULL,
                           &thread_serial1_task_stack[0],
                           sizeof(thread_serial1_task_stack),PRIORITY_UART,5);
    if (res == RT_EOK)
        rt_thread_startup(&thread_serial1_task_handle);

    res = rt_thread_init(&thread_serial2_task_handle,
                           "serial2",
                           serial2_main,
                           RT_NULL,
                           &thread_serial2_task_stack[0],
                           sizeof(thread_serial2_task_stack),PRIORITY_UART,5);
    if (res == RT_EOK)
        rt_thread_startup(&thread_serial2_task_handle);

    res = rt_thread_init(&thread_serial3_task_handle,
                           "serial3",
                           serial3_main,
                           RT_NULL,
                           &thread_serial3_task_stack[0],
                           sizeof(thread_serial3_task_stack),PRIORITY_UART,5);
    if (res == RT_EOK)
        rt_thread_startup(&thread_serial3_task_handle);

    res = rt_thread_init(&thread_serial4_task_handle,
                           "serial4",
                           serial4_main,
                           RT_NULL,
                           &thread_serial4_task_stack[0],
                           sizeof(thread_serial4_task_stack),PRIORITY_UART,5);
    if (res == RT_EOK)
        rt_thread_startup(&thread_serial4_task_handle);

    res = rt_thread_init(&thread_serial5_task_handle,
                           "serial5",
                           serial5_main,
                           RT_NULL,
                           &thread_serial5_task_stack[0],
                           sizeof(thread_serial5_task_stack),PRIORITY_UART,5);
    if (res == RT_EOK)
        rt_thread_startup(&thread_serial5_task_handle);

    return RT_EOK;
}

/*------------------------------------test------------------------------------*/


